home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
Source.bin
/
SplitterPanel.java
< prev
next >
Wrap
Text File
|
1998-09-23
|
84KB
|
2,674 lines
package symantec.itools.awt;
import java.awt.Panel;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.LayoutManager;
import java.awt.Graphics;
import java.awt.Container;
import java.awt.Event;
import java.awt.Cursor;
import java.beans.PropertyVetoException;
import java.beans.PropertyChangeEvent;
import java.beans.VetoableChangeListener;
import java.beans.PropertyChangeListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ResourceBundle;
// 05/07/97 CAR Added implementation for getComponents() which is now called by AWT code.
// 07/17/97 LAB Updated version to 1.1. Updated event handling to JDK 1.1. Bound and constrained
// properties. Added add/removeNotify for listener registration. Changed references
// to setCursor to use Component's setCursor rather than Frame's. Added MoveSplitCursor
// property. Added Use3DBorder property. Added AllowDynamicMoving property.
// 07/28/97 CAR marked fields transient as needed
// inner classes implement java.io.Serializable
// 08/29/97 CAR modified preferredSize
/**
* Use to create a container that can be divided into a number of subpanels which
* holds visual components and other panels, and specifically to:
* <UL>
* <DT>╖ Create a subcontainer that organizes container space within an Applet,
* Frame or Dialog container. This simplifies your component layout task.</DT>
* <DT>╖ Hold other specialized Panel containers.</DT>
* </UL>
* The SplitterPanel component is the parent Panel containing a set of
* subpanels. You must write code to divide the parent panel into subpanels.
* You must set panel defaults in your project source code.
* <p>
* The SplitterPanel automatically recognizes the mouseDragged as the command
* to resize the appropriate subpanel boundary.
* <p>
* Subpanels can be resized at runtime by dragging panel borders with the mouse.
* <p>
* This code example splits a SplitterPanel into four subpanels and adds a
* button to two of the subpanels.
* <UL><pre>
* splitterPanel1.split(splitterPanel1.SPLIT_HORIZONTAL);
* splitterPanel1.getTopPanel().split(splitterPanel1.SPLIT_VERTICAL);
* splitterPanel1.getBottomPanel().split(splitterPanel1.SPLIT_VERTICAL);
* // add a button to the top left panel
* splitterPanel1.getTopLeftPanel().add(new Button("Top Left"));
* // add a button to the lower right panel
* splitterPanel1.getBottomRightPanel().add(new Button("Bottom Right"));
* </pre></UL>
* Individual subpanels can be accessed in code using any of these methods:
* getBottomLeftPanel,
* getBottomPanel,
* getBottomRightPanel,
* getLeftPanel,
* getRightPanel,
* getSub2Panel,
* getSubPanel,
* getTopLeftPanel,
* getTopPanel,
* getTopRightPanel.
* <p>
*
* @version 1.1, July 15, 1997
* @author Symantec
*/
public class SplitterPanel extends Panel
{
/**
* Vertical panel split constant for the SplitterPanel constructor.
* This splits the panel into two vertical panels.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT_VERTICAL = 1;
/**
* Horizontal panel split constant for the SplitterPanel constructor.
* This splits the panel into two horizontal panels.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT_HORIZONTAL = 2;
/**
* Indicates a vertical panel split. The panel contains/will
* contain two subpanels, left and right.
* @see #split
*/
public static final int SPLIT_VER = SPLIT_VERTICAL;
/**
* Indicates a horizontal panel split. The panel contains/will
* contain two subpanels, top and bottom.
* @see #split
*/
public static final int SPLIT_HOR = SPLIT_HORIZONTAL;
/**
* Panel split constant for the SplitterPanel constructor.
* This splits the panel into four panels by first splitting horizontally then
* splitting vertically.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT_BOTH = 3;
/**
* Panel split constant for the SplitterPanel constructor.
* This splits the panel into four panels by first splitting vertically then
* splitting horizontally.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT_BOTH_V = 4;
/**
* Panel split constant for the SplitterPanel constructor.
* This splits the panel into three panels, with a single larger panel on the
* right.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT3_LEFT = 5;
/**
* Panel split constant for the SplitterPanel constructor.
* This splits the panel into three panels, with a single larger panel on the
* left.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT3_RIGHT = 6;
/**
* Panel split constant for the SplitterPanel constructor.
* This splits the panel into three panels, with a single larger panel on the
* bottom.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT3_TOP = 7;
/**
* Panel split constant for the SplitterPanel constructor.
* This splits the panel into three panels, with a single larger panel on the
* top.
*/
// @see symantec.itools.awt.SplitterPanel#SplitterPanel(int, int, int)
public static final int SPLIT3_BOTTOM = 8;
/**
* Constructs a new default SplitterPanel.
* By default, all the gap sizes are 3 and all the border sizes 2.
* @see #setGapSizes(int, int, int, int)
* @see #setBdrSizes(int, int)
*/
public SplitterPanel()
{
this(true);
}
/**
* Constructs a new SplitterPanel of the specified size which
* is ready to be split into panels.
* By default, all the gap sizes are 3 and all the border sizes 2.
* @param spWidth the initial width of the SplitterPanel, in pixels
* @param spHeight the initial height of the SplitterPanel, in pixels
* @see #setGapSizes(int, int, int, int)
* @see #setBdrSizes(int, int)
*/
public SplitterPanel(int spWidth, int spHeight)
{
this(true);
if (this == null)
return;
resize(spWidth, spHeight);
}
/**
* Constructs a new SplitterPanel of specified size which is
* split into panels.
* By default, all the gap sizes are 3 and all the border sizes 2.
* @param spWidth the initial width of the SplitterPanel, in pixels
* @param spHeight the initial height of the SplitterPanel, in pixels
* @param splitSpec specifies how to split up the panel
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
* @see #SPLIT_BOTH
* @see #SPLIT_BOTH_V
* @see #SPLIT3_LEFT
* @see #SPLIT3_RIGHT
* @see #SPLIT3_TOP
* @see #SPLIT3_BOTTOM
* @see #setGapSizes(int, int, int, int)
* @see #setBdrSizes(int, int)
*/
public SplitterPanel(int spWidth, int spHeight, int splitSpec)
{
this(splitSpec);
if (this == null)
return;
resize(spWidth, spHeight);
}
/**
* Constructs a new SplitterPanel which is split into panels.
* By default, all the gap sizes are 3 and all the border sizes 2.
* @param splitSpec specifies how to split up the panel
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
* @see #SPLIT_BOTH
* @see #SPLIT_BOTH_V
* @see #SPLIT3_LEFT
* @see #SPLIT3_RIGHT
* @see #SPLIT3_TOP
* @see #SPLIT3_BOTTOM
* @see #setGapSizes(int, int, int, int)
* @see #setBdrSizes(int, int)
*/
public SplitterPanel(int splitSpec)
{
this(splitSpec, null, null, null, null);
}
/**
* Constructs a new SplitterPanel which is split into panels
* to which components are added.
* By default, all the gap sizes are 3 and all the border sizes 2.
* @param splitSpec specifies how to split up the panel
* @param compR1C1 specifies the component in row 1, column 1 (top, left)
* @param compR1C2 specifies the component in row 1, column 2 (top, right)
* @param compR2C1 specifies the component in row 2, column 1 (bottom, left)
* @param compR2C2 specifies the component in row 2, column 2 (bottom, right)
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
* @see #SPLIT_BOTH
* @see #SPLIT_BOTH_V
* @see #SPLIT3_LEFT
* @see #SPLIT3_RIGHT
* @see #SPLIT3_TOP
* @see #SPLIT3_BOTTOM
* @see #setGapSizes(int, int, int, int)
* @see #setBdrSizes(int, int)
*/
public SplitterPanel(int splitSpec, Component compR1C1, Component compR1C2, Component compR2C1, Component compR2C2)
{
this(true);
if (this == null)
return;
switch (splitSpec)
{
case SPLIT_NONE:
{
if (compR1C1 != null) this.add(compR1C1);
break;
}
case SPLIT_VERTICAL:
{
split(SPLIT_VER, compR1C1, compR1C2);
break;
}
case SPLIT_HORIZONTAL:
{
split(SPLIT_HOR, compR1C1, compR2C1);
break;
}
case SPLIT_BOTH:
{
split(SPLIT_HOR);
getSubPanel(1).split(SPLIT_VER, compR1C1, compR1C2);
getSubPanel(2).split(SPLIT_VER, compR2C1, compR2C2);
break;
}
case SPLIT_BOTH_V:
{
split(SPLIT_VER);
getSubPanel(1).split(SPLIT_HOR, compR1C1, compR2C1);
getSubPanel(2).split(SPLIT_HOR, compR1C2, compR2C2);
break;
}
case SPLIT3_LEFT:
{
split(SPLIT_VER, null, compR1C2);
getSubPanel(1).split(SPLIT_HOR, compR1C1, compR2C1);
break;
}
case SPLIT3_RIGHT:
{
split(SPLIT_VER, compR1C1, null);
getSubPanel(2).split(SPLIT_HOR, compR1C2, compR2C2);
break;
}
case SPLIT3_TOP:
{
split(SPLIT_HOR, null, compR2C1);
getSubPanel(1).split(SPLIT_VER, compR1C1, compR1C2);
break;
}
case SPLIT3_BOTTOM:
{
split(SPLIT_HOR, compR1C1, null);
getSubPanel(2).split(SPLIT_VER, compR2C1, compR2C2);
break;
}
}
}
private SplitterPanel(boolean isOuter)
{
super();
this.isOuter = isOuter;
heightOnly = false;
widthOnly = false;
allowDynamicMoving = false;
splitType = SPLIT_NONE;
// Set defaults
minDim = new Dimension(0, 0);
curDim = new Dimension(0, 0);
curLoc = new Point(0, 0);
prefDim = new Dimension(0, 0);
try
{
setGapColor(Color.lightGray);
}
catch (PropertyVetoException exc) {}
iGapWidth = 3;
iGapHeight = 3;
iBdrSize = 2;
oGapWidth = 3;
oGapHeight = 3;
oBdrSize = 2;
//If outer, then create the REQUIRED first inner Panel
if (isOuter)
{
innerSP = new SplitterPanel(false);
super.add(innerSP, -1);
outerSP = this;
innerSP.outerSP = this;
layout();
}
}
//--------------------------------------------------
// accessor methods
//--------------------------------------------------
/**
* This flag specifies how to draw the border around panels.
* @param flag if true, the border around the panel is drawn in a three dimensional way.
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #isUse3DBorder
*/
public void setUse3DBorder(boolean flag) throws PropertyVetoException
{
if(use3dBdr != flag)
{
Boolean oldValue = new Boolean(use3dBdr);
Boolean newValue = new Boolean(flag);
vetos.fireVetoableChange("Use3DBorder", oldValue, newValue);
use3dBdr = flag;
changes.firePropertyChange("Use3DBorder", oldValue, newValue);
}
}
/**
* This flag specifies how to draw the border around panels.
* @return flag if true, the border around the panel is drawn in a three dimensional way.
* @see #setUse3DBorder
*/
public boolean isUse3DBorder()
{
return use3dBdr;
}
/**
* This flag specifies if moving of a split in a splitter panel will be real time, or delayed until mouse released.
* @param flag if true, when a split is moved, it will dynamically resize the panels the split affects. If false,
* the resizing will happen when the mouse is released.
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #isAllowDynamicMoving
*/
public void setAllowDynamicMoving(boolean flag) throws PropertyVetoException
{
if(allowDynamicMoving != flag)
{
Boolean oldValue = new Boolean(allowDynamicMoving);
Boolean newValue = new Boolean(flag);
vetos.fireVetoableChange("AllowDynamicMoving", oldValue, newValue);
allowDynamicMoving = flag;
propagateChanges();
changes.firePropertyChange("AllowDynamicMoving", oldValue, newValue);
}
}
/**
* This flag specifies if moving of a split in a splitter panel will be real time, or delayed until mouse released.
* @return true if when a split is moved, it will dynamically resize the panels the split affects. If false,
* the resizing will happen when the mouse is released.
* @see #setAllowDynamicMoving
*/
public boolean isAllowDynamicMoving()
{
return allowDynamicMoving;
}
/**
* Gets the type of split for this SplitterPanel.
* @return the splitType: SPLIT_VERTICAL or SPLIT_HORIZONTAL
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
*/
public int getSplitType()
{
SplitterPanel targetSP = this;
if (isOuter) targetSP = this.innerSP;
return targetSP.splitType;
}
/**
* Determines the cursor to use when dragging the split between panels.
* Use Cursor.HAND_CURSOR, etc to choose a cursor
* @param newCursor the new Cursor to use.
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #getMoveSplitCursor
* @see #setMoveSplitCursor(int)
*/
public void setMoveSplitCursor(Cursor newCursor) throws PropertyVetoException
{
if(!symantec.itools.util.GeneralUtils.objectsEqual(moveSplitCursor, newCursor))
{
Cursor oldValue = moveSplitCursor;
vetos.fireVetoableChange("MoveSplitCursor", oldValue, newCursor);
moveSplitCursor = newCursor;
changes.firePropertyChange("MoveSplitCursor", oldValue, newCursor);
}
}
/**
* Determines the cursor to use when dragging the split between panels.
* Use Cursor.HAND_CURSOR, etc to choose a cursor
* @param newCursor the integer value of a Cursor to use.
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #getMoveSplitCursor
* @see #setMoveSplitCursor(java.awt.Cursor)
* @see java.awt.Cursor#DEFAULT_CURSOR
* @see java.awt.Cursor#CROSSHAIR_CURSOR
* @see java.awt.Cursor#TEXT_CURSOR
* @see java.awt.Cursor#WAIT_CURSOR
* @see java.awt.Cursor#SW_RESIZE_CURSOR
* @see java.awt.Cursor#SE_RESIZE_CURSOR
* @see java.awt.Cursor#NW_RESIZE_CURSOR
* @see java.awt.Cursor#NE_RESIZE_CURSOR
* @see java.awt.Cursor#N_RESIZE_CURSOR
* @see java.awt.Cursor#S_RESIZE_CURSOR
* @see java.awt.Cursor#W_RESIZE_CURSOR
* @see java.awt.Cursor#E_RESIZE_CURSOR
* @see java.awt.Cursor#HAND_CURSOR
* @see java.awt.Cursor#MOVE_CURSOR
*/
public void setMoveSplitCursor(int newCursor) throws PropertyVetoException, IllegalArgumentException
{
setMoveSplitCursor(new Cursor(newCursor));
}
/**
* The cursor to use when dragging the split between panels.
* @return the Cursor to be used when dragginf the split between panels.
* @see #setMoveSplitCursor(java.awt.Cursor)
* @see #setMoveSplitCursor(int)
*/
public Cursor getMoveSplitCursor()
{
return moveSplitCursor;
}
/**
* Sets the color of the gap between panels and around the outside
* border.
* @param c the color to use
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #getGapColor
*/
public void setGapColor(Color c) throws PropertyVetoException
{
if(!symantec.itools.util.GeneralUtils.objectsEqual(gapColor, c))
{
Color oldValue = this.gapColor;
vetos.fireVetoableChange("GapColor", oldValue, c);
this.gapColor = new Color(c.getRed(), c.getGreen(), c.getBlue());
propagateChanges();
changes.firePropertyChange("GapColor", oldValue, c);
}
}
/**
* Gets the color of the gap between panels and around the outside border.
* @return the current gap color
* @see #setGapColor
*/
public Color getGapColor()
{
return this.gapColor;
}
/**
* @deprecated
* @see getGapColor
*/
public Color gapColor()
{
return getGapColor();
}
/**
* Sets the "enforce minimum dimension" mode.
* This mode prevents the dragging of a split between panels
* from making a panel smaller than the minimum size of its component.
* @param theFlag if true enforces minimum panel dimensions
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #isEnforceMinDim
*/
public void setEnforceMinDim(boolean theFlag) throws PropertyVetoException
{
if(enforceMinDim != theFlag)
{
Boolean oldValue = new Boolean(enforceMinDim);
Boolean newValue = new Boolean(theFlag);
vetos.fireVetoableChange("EnforceMinDim", oldValue, newValue);
enforceMinDim = theFlag;
propagateChanges();
changes.firePropertyChange("EnforceMinDim", oldValue, newValue);
}
}
/**
* Gets the current "enforce minimum dimension" mode.
* This mode prevents the dragging of a split between panels
* from making a panel smaller than the minimum size of its component.
* @return true if minimum panel dimensions are enforced
* @see #setEnforceMinDim
*/
public boolean isEnforceMinDim()
{
return enforceMinDim;
}
/**
* @deprecated
* @see #isEnforceMinDim
*/
public boolean getEnforceMinDim()
{
return isEnforceMinDim();
}
/**
* Sets the "resize propagation" mode.
* Set this to false to prevent the automatic resizing of SplitterPanel
* panels from also calling resize() for the component that you add.
* The default behavior propagates the resizing effect of dragging the
* split between panes by calling move and resize for the panels that have
* been added to those panes. Clearing this introduces a clipping effect,
* rather than a scaling effect.
* @param theFlag if true, resize propagation is enabled; if false,
* resize propagation is disabled
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #isPropResize
*/
public void setPropResize(boolean theFlag) throws PropertyVetoException
{
if(propResize != theFlag)
{
Boolean oldValue = new Boolean(propResize);
Boolean newValue = new Boolean(theFlag);
vetos.fireVetoableChange("PropResize", oldValue, newValue);
//Controls whether reshapes are propagated into added components
propResize = theFlag;
propagateChanges();
changes.firePropertyChange("PropResize", oldValue, newValue);
}
}
/**
* Gets the current "resize propagation" mode.
* When this mode is enabled, the resizing effect of dragging the split
* between panes is propagated by reshaping the panels that have been
* added to those panes. This results in a scaling effect.
* When this mode is disabled this doesn't happen, resulting in a
* clipping effect.
* This mode is enabled by default.
* @return the current "resize propagation" mode
* @see #setPropResize
*/
public boolean isPropResize()
{
return propResize;
}
/**
* @deprecated
* @see #isPropResize
*/
public boolean getPropResize()
{
return isPropResize();
}
/**
* Sets the size of the split between panels and around the inside of the
* SplitterPanel border.
* The default for all gap sizes is 3.
* @param gapSize the split size in pixels
*/
public void setGapSizes(int gapSize)
{
this.iGapWidth = gapSize;
this.iGapHeight = gapSize;
this.oGapWidth = gapSize;
this.oGapHeight = gapSize;
adjustOGapBdr();
propagateChanges();
repaint();
}
/**
* Sets the size of the split between panels and around the inside of the
* SplitterPanel border.
* The default for all gap sizes is 3.
* @param gapWidth the split width in pixels
* @param gapHeight the split height in pixels
*/
public void setGapSizes(int gapWidth, int gapHeight)
{
this.iGapWidth = gapWidth;
this.iGapHeight = gapHeight;
this.oGapWidth = gapWidth;
this.oGapHeight = gapHeight;
adjustOGapBdr();
propagateChanges();
repaint();
}
/**
* Sets the size of the split between panels and around the inside of the
* SplitterPanel border.
* The default for all gap sizes is 3.
* @param iGapWidth the split width between panels, in pixels
* @param iGapHeight the split height between panels, in pixels
* @param oGapWidth the gap width inside the border, in pixels
* @param oGapHeight the gap height inside the border, in pixels
*/
public void setGapSizes(int iGapWidth, int iGapHeight, int oGapWidth, int oGapHeight)
{
this.iGapWidth = iGapWidth;
this.iGapHeight = iGapHeight;
this.oGapWidth = oGapWidth;
this.oGapHeight = oGapHeight;
adjustOGapBdr();
propagateChanges();
repaint();
}
/**
* Sets the size of the border drawn between panels and around the
* outside of the SplitterPanel.
* The default for all border sizes is 2.
* @param iBdrSize the border size between panels, in pixels
* @param oBdrSize the border size around the outside of the SplitterPanel, in pixels
*/
public void setBdrSizes(int iBdrSize, int oBdrSize)
{
this.iBdrSize = iBdrSize;
this.oBdrSize = oBdrSize;
adjustOGapBdr();
propagateChanges();
repaint();
}
/**
* Sets the size of the border drawn between panels and around the
* outside of the SplitterPanel.
* @param bdrSize the size in pixels
*/
public void setBdrSizes(int bdrSize)
{
this.iBdrSize = bdrSize;
this.oBdrSize = bdrSize;
adjustOGapBdr();
propagateChanges();
repaint();
}
/**
* Gets a panel within a SplitterPanel.
* @param n panel number (1 for first panel, or 2 for second
* panel)
* @return the panel, or null if none
* @see #getSub2Panel
*/
public SplitterPanel getSubPanel(int n)
{
SplitterPanel targetSP = this;
if (isOuter)
targetSP = this.innerSP;
if (n == 1)
return targetSP.sub1;
if (n == 2)
return targetSP.sub2;
return null;
}
/**
* Gets a panel within a SplitterPanel of specified type.
* For example, if "aPanel" was a horizontally split SplitterPanel,
* the call aPanel.getSubPanel(1,SPLIT_HOR) would retrieve the first
* (topmost) panel, and the call aPanel.getSubPanel(1,SPLIT_VER) would
* return null since aPanel is not split vertically.
*
* @param n panel number (1 for first panel, or 2 for second
* panel)
* @param theSplitType the type of split this panel has
* (SPLIT_VER or SPLIT_HOR)
* @return the panel, or null if none of the specified type
* @see #SPLIT_VER
* @see #SPLIT_HOR
* @see #getSub2Panel
*/
public SplitterPanel getSubPanel(int n, int theSplitType)
{
if (getSplitType() == theSplitType)
return getSubPanel(n);
return null;
}
/**
* Gets a panel within a doubly split SplitterPanel with
* specified split types.
* This method is equivalent to calling getSubPanel() twice, the
* second time with the results from the first.
* @param n1 panel number (1 for first panel, or 2 for second panel)
* @param theSplitType1 the type of split this panel has
* (SPLIT_VER or SPLIT_HOR)
* @param n2 panel number (1 for first panel, or 2 for second panel)
* @param theSplitType2 the type of split the subpanel has
* (SPLIT_VER or SPLIT_HOR)
* @return the panel, or null if none of the specified type
* @see #SPLIT_VER
* @see #SPLIT_HOR
* @see #getSubPanel(int, int)
*/
public SplitterPanel getSub2Panel(int n1, int theSplitType1, int n2, int theSplitType2)
{
SplitterPanel theSP;
theSP = getSubPanel(n1, theSplitType1);
if (theSP != null)
return theSP.getSubPanel(n2, theSplitType2);
return null;
}
/**
* Gets the top panel of a horizontally split SplitterPanel.
* @return the panel, or null if this panel is not split or is
* split vertically
* @see #getBottomPanel
* @see #getLeftPanel
* @see #getRightPanel
* @see #getTopLeftPanel
* @see #getTopRightPanel
* @see #getBottomLeftPanel
* @see #getBottomRightPanel
*/
public SplitterPanel getTopPanel()
{
return getSubPanel(1, SPLIT_HOR);
}
/**
* Gets the bottom panel of a horizontally split SplitterPanel.
* @return the panel, or null if this panel is not split or is
* split vertically
* @see #getTopPanel
* @see #getLeftPanel
* @see #getRightPanel
* @see #getTopLeftPanel
* @see #getTopRightPanel
* @see #getBottomLeftPanel
* @see #getBottomRightPanel
*/
public SplitterPanel getBottomPanel()
{
return getSubPanel(2, SPLIT_HOR);
}
/**
* Gets the left panel of a vertically split SplitterPanel.
* @return the panel, or null if this panel is not split or is
* split horizontally
* @see #getTopPanel
* @see #getBottomPanel
* @see #getRightPanel
* @see #getTopLeftPanel
* @see #getTopRightPanel
* @see #getBottomLeftPanel
* @see #getBottomRightPanel
*/
public SplitterPanel getLeftPanel()
{
return getSubPanel(1, SPLIT_VER);
}
/**
* Gets the right panel of a vertically split SplitterPanel.
* @return the panel, or null if this panel is not split or is
* split horizontally
* @see #getTopPanel
* @see #getBottomPanel
* @see #getLeftPanel
* @see #getTopLeftPanel
* @see #getTopRightPanel
* @see #getBottomLeftPanel
* @see #getBottomRightPanel
*/
public SplitterPanel getRightPanel()
{
return getSubPanel(2, SPLIT_VER);
}
/**
* Gets the top left panel.
* It doesn't matter if this SplitterPanel is initially split horizontally
* or vertically.
* @return the panel, or null if it doesn't exist
* @see #getTopPanel
* @see #getBottomPanel
* @see #getLeftPanel
* @see #getRightPanel
* @see #getTopRightPanel
* @see #getBottomLeftPanel
* @see #getBottomRightPanel
*/
public SplitterPanel getTopLeftPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(1, SPLIT_HOR, 1, SPLIT_VER);
return getSub2Panel(1, SPLIT_VER, 1, SPLIT_HOR);
}
/**
* Gets the top right panel.
* It doesn't matter if this SplitterPanel is initially split horizontally
* or vertically.
* @return the panel, or null if it doesn't exist
* @see #getTopPanel
* @see #getBottomPanel
* @see #getLeftPanel
* @see #getRightPanel
* @see #getTopLeftPanel
* @see #getBottomLeftPanel
* @see #getBottomRightPanel
*/
public SplitterPanel getTopRightPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(1, SPLIT_HOR, 2, SPLIT_VER);
return getSub2Panel(2, SPLIT_VER, 1, SPLIT_HOR);
}
/**
* Gets the bottom left panel.
* It doesn't matter if this SplitterPanel is initially split horizontally
* or vertically.
* @return the panel, or null if it doesn't exist
* @see #getTopPanel
* @see #getBottomPanel
* @see #getLeftPanel
* @see #getRightPanel
* @see #getTopLeftPanel
* @see #getTopRightPanel
* @see #getBottomRightPanel
*/
public SplitterPanel getBottomLeftPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(2, SPLIT_HOR, 1, SPLIT_VER);
return getSub2Panel(1, SPLIT_VER, 2, SPLIT_HOR);
}
/**
* Gets the bottom right panel.
* It doesn't matter if this SplitterPanel is initially split horizontally
* or vertically.
* @return the panel, or null if it doesn't exist
* @see #getTopPanel
* @see #getBottomPanel
* @see #getLeftPanel
* @see #getRightPanel
* @see #getTopLeftPanel
* @see #getTopRightPanel
* @see #getBottomLeftPanel
*/
public SplitterPanel getBottomRightPanel()
{
if (getSplitType() == SPLIT_HOR)
return getSub2Panel(2, SPLIT_HOR, 2, SPLIT_VER);
return getSub2Panel(2, SPLIT_VER, 2, SPLIT_HOR);
}
/**
* Gets the size of the split between panels.
* @return the split width between panels, in pixels
* @see #iGapHeight()
* @see #oGapWidth()
* @see #oGapHeight()
*/
public int iGapWidth()
{
return this.iGapWidth;
}
/**
* Gets the size of the split between panels.
* @return the split height between panels, in pixels
* @see #iGapWidth()
* @see #oGapWidth()
* @see #oGapHeight()
*/
public int iGapHeight()
{
return this.iGapHeight;
}
/**
* Gets the size of the split around the inside of the SplitterPanel border.
* @return the gap width inside the border, in pixels
* @see #iGapWidth()
* @see #iGapHeight()
* @see #oGapHeight()
*/
public int oGapWidth()
{
return this.oGapWidth;
}
/**
* Gets the size of the split around the inside of the SplitterPanel border.
* @return the gap height inside the border, in pixels
* @see #iGapWidth()
* @see #iGapHeight()
* @see #oGapWidth()
*/
public int oGapHeight()
{
return this.oGapHeight;
}
/**
* Gets the size of the border drawn between panels.
* @return the border size between panels, in pixels
* @see #oBdrSize()
*/
public int iBdrSize()
{
return this.iBdrSize;
}
/**
* Gets the size of the border drawn around the outside of the SplitterPanel.
* @return the border size around the outside of the SplitterPanel, in pixels
* @see #iBdrSize()
*/
public int oBdrSize()
{
return this.oBdrSize;
}
/**
* Handles the laying out of components within this component.
* This is a standard Java AWT method which gets called by the AWT
* when this component is validated with the validate() method.
*
* @see java.awt.Container#validate
*/
public synchronized void layout()
{
placeComponents();
}
/**
* Moves the location of the gap between panels.
* @param spX specifies relative movement in pixels along horizontal axis
* @param spY specifies relative movement in pixels along vertical axis
*/
public void moveSplit(int spX, int spY)
{
Rectangle cur1;
Rectangle cur2;
Dimension min1;
Dimension min2;
int delta;
int newX1;
int newY1;
int newWidth1;
int newHeight1;
int newX2;
int newY2;
int newWidth2;
int newHeight2;
if (isOuter)
{
innerSP.moveSplit(spX, spY);
return;
}
else
{
min1 = sub1.minimumSize();
min2 = sub2.minimumSize();
cur1 = new Rectangle(sub1.curLoc.x, sub1.curLoc.y, sub1.curDim.width, sub1.curDim.height);
cur2 = new Rectangle(sub2.curLoc.x, sub2.curLoc.y, sub2.curDim.width, sub2.curDim.height);
newX1 = cur1.x;
newY1 = cur1.y;
if (splitType == SPLIT_HOR)
{
if (spY == 0) return;
if (spY > 0)
{
if (enforceMinDim)
{
newHeight2 = Math.max(cur2.height - spY, min2.height);
}
else
{
newHeight2 = Math.max(cur2.height - spY, 2*iBdrSize);
}
newHeight1 = cur1.height + cur2.height - newHeight2;
}
else
{
if (enforceMinDim)
{
newHeight1 = Math.max(cur1.height + spY, min1.height);
}
else
{
newHeight1 = Math.max(cur1.height + spY, 2*iBdrSize);
}
newHeight2 = cur2.height + cur1.height - newHeight1;
}
newWidth1 = cur1.width;
newWidth2 = cur2.width;
newX2 = cur2.x;
newY2 = cur2.y - cur1.height + newHeight1;
sub1.reshapeHeight(newY1, newHeight1);
sub2.reshapeHeight(newY2, newHeight2);
}
if (splitType == SPLIT_VER)
{
if (spX == 0) return;
if (spX > 0)
{
if (enforceMinDim)
{
newWidth2 = Math.max(cur2.width - spX, min2.width);
}
else
{
newWidth2 = Math.max(cur2.width - spX, 2*iBdrSize);
}
newWidth1 = cur1.width + cur2.width - newWidth2;
}
else
{
if (enforceMinDim)
{
newWidth1 = Math.max(cur1.width + spX, min1.width);
}
else
{
newWidth1 = Math.max(cur1.width + spX, 1*iBdrSize);
}
newWidth2 = cur2.width + cur1.width - newWidth1;
}
newHeight1 = cur1.height;
newHeight2 = cur2.height;
newY2 = cur2.y;
newX2 = cur2.x - cur1.width + newWidth1;
sub1.reshapeWidth(newX1, newWidth1);
sub2.reshapeWidth(newX2, newWidth2);
}
sub1.invalidate();
sub1.validate();
sub1.repaint();
sub2.invalidate();
sub2.validate();
sub2.repaint();
}
}
/**
* Handles redrawing of this component on the screen.
* This is a standard Java AWT method which gets called by the Java
* AWT (repaint()) to handle repainting this component on the screen.
* The graphics context clipping region is set to the bounding rectangle
* of this component and its [0,0] coordinate is this component's
* top-left corner.
* Typically this method paints the background color to clear the
* component's drawing space, sets graphics context to be the foreground
* color, and then calls paint() to draw the component.
*
* It is overridden here to reduce flicker by eliminating the unnecessary
* clearing of the background.
*
* @param g the graphics context
* @see java.awt.Component#repaint
* @see #paint
*/
public void update(Graphics g)
{
paint(g);
}
/**
* Paints this component using the given graphics context.
* This is a standard Java AWT method which typically gets called
* by the AWT to handle painting this component. It paints this component
* using the given graphics context. The graphics context clipping region
* is set to the bounding rectangle of this component and its [0,0]
* coordinate is this component's top-left corner.
*
* Overriding this method is not advised.
*
* @param g the graphics context used for painting
* @see java.awt.Component#repaint
* @see #update
*/
public void paint(Graphics g)
{
Color bgColor;
Color fgColor;
Dimension d = size();
bgColor = getBackground();
g.setColor(bgColor);
if (isOuter)
{
g.setColor(gapColor);
g.fillRect(0, 0, d.width, d.height);
draw3DBdr(g, 0, 0, d.width-1, d.height-1, oBdrSize, gapColor, true);
}
else if (splitType == SPLIT_NONE)
{
g.setColor(bgColor);
g.fillRect(0, 0, d.width, d.height );
draw3DBdr(g, 0, 0, d.width-1, d.height-1, iBdrSize, gapColor, false);
}
else
{
g.setColor(gapColor);
g.fillRect(0, 0, d.width, d.height );
}
super.paint(g);
}
/**
* Gets the component in this SplitterPanel.
* @return the component in this SplitterPanel
*/
public Component getComponent()
{
if (isOuter)
return innerSP.getComponent();
return spComponent;
}
/**
* Returns all of the components in this container.
* This is a standard Java AWT method which gets called to return
* an array of all of the components in this container.
*
* @return an array of components in this container
*/
public Component[] getComponents()
{
if (isOuter)
return innerSP.getComponents();
return super.getComponents();
}
/**
* Returns number of components in this container.
*
* @return the number of components in this container
*/
public int getComponentCount()
{
if (isOuter)
return innerSP.getComponentCount();
return super.getComponentCount();
}
/**
* Returns the recommended dimensions to properly display this component.
* This is a standard Java AWT method which gets called to determine
* the recommended size of this component.
* <p>
* For each axis, returns the larger of the actual size needed to show all
* the components at their preferred size, and the preferred size set by
* the setPreferredSize() method.
*
* @see #minimumSize
* @see #setPreferredSize
*/
public Dimension preferredSize()
{
Dimension p = size();
Dimension m = getMinimumSize();
return new Dimension(Math.max(p.width, m.width), Math.max(p.height, m.height));
}
/**
* Returns the minimum dimensions to properly display this component.
* This is a standard Java AWT method which gets called to determine
* the minimum size of this component.
* <p>
* For each axis, returns the larger of the actual size needed to show all
* the components at their minimum size, and the minimum size set by
* the setMinimumSize() method.
*
* @see #preferredSize
*/
public Dimension minimumSize()
{
Dimension theDim;
if (isOuter)
{
theDim = new Dimension(innerSP.minimumSize());
theDim.width += 2*(oGapWidth + oBdrSize);
theDim.height += 2*(oGapHeight + oBdrSize);
}
else
{
if (splitType == SPLIT_NONE)
{
if (spComponent == null)
{
theDim = new Dimension(0, 0);
}
else
{
theDim = new Dimension(spComponent.minimumSize());
}
theDim.width += 2*(iBdrSize);
theDim.height += 2*(iBdrSize);
}
else
{
Dimension theDim1;
Dimension theDim2;
theDim1 = sub1.minimumSize();
theDim2 = sub2.minimumSize();
if (splitType == SPLIT_HOR)
{
theDim = new Dimension(theDim1.width, theDim1.height + theDim2.height);
theDim.height += iGapHeight;
}
else
{
theDim = new Dimension(theDim1.width + theDim1.width, theDim1.height);
theDim.width += iGapWidth;
}
}
}
return maxDimOf(minDim, theDim);
}
/**
* Sets the preferred size of this SplitterPanel.
* This will override the calculated preferred size when the calculated
* size is smaller.
* @param theDim the preferred dimensions
*/
public void setPreferredSize(Dimension theDim)
{
prefDim.width = theDim.width;
prefDim.height = theDim.height;
//Must ensure that preferred size is as big as minimum size
prefDim = maxDimOf(theDim, prefDim);
layout();
}
/**
* Sets the minimum size of this SplitterPanel.
* This will override the calculated minimum size when the calculated
* size is smaller.
* @param theDim the minimum dimensions
*/
public void setMinimumSize(Dimension theDim)
{
minDim.width = theDim.width;
minDim.height = theDim.height;
prefDim = maxDimOf(theDim, prefDim);
layout();
}
/**
* Moves and/or resizes this component.
* This is a standard Java AWT method which gets called to move and/or
* resize this component. Components that are in containers with layout
* managers should not call this method, but rely on the layout manager
* instead.
*
* @param x horizontal position in the parent's coordinate space
* @param y vertical position in the parent's coordinate space
* @param width the new width
* @param height the new height
* @see #reshapeHeight
* @see #reshapeWidth
*/
public synchronized void reshape (int x, int y, int width, int height) {
super.reshape(x, y, width, height);
if ((curDim.width != width) || (curDim.height != height) ||
(curLoc.x != x) || (curLoc.y != y))
{
layout();
curDim.width = width;
curDim.height = height;
curLoc.x = x;
curLoc.y = y;
}
}
/**
* Reshapes the width and/or horizontal position.
* This prevents vertical adjustments when the layout is computed.
* @param x horizontal position in the parent's coordinate space
* @param width the new width, in pixels
* @see #reshapeHeight
* @see #reshape
*/
public synchronized void reshapeWidth(int x, int width) {
Rectangle rect = new Rectangle(curLoc.x, curLoc.y, curDim.width, curDim.height);
if (curDim.width != width)
{
widthOnly = true;
curDim.width = width;
}
reshape(x, rect.y, width, rect.height);
}
/**
* Reshapes the height and/or vertical position.
* This prevents horizontal adjustments when the layout is computed.
* @param y vertical position in the parent's coordinate space
* @param height the new height, in pixels
* @see #reshapeWidth
* @see #reshape
*/
public synchronized void reshapeHeight(int y, int height) {
Rectangle rect = new Rectangle(curLoc.x, curLoc.y, curDim.width, curDim.height);
if (curDim.height != height)
{
heightOnly = true;
curDim.height = height;
}
reshape(rect.x, y, rect.width, height);
}
/**
* Adds a component to the end of this container.
* This is a standard Java AWT method which gets called to add a
* component to a container. Typically, the specified component is added to
* this container at the given zero-relative position index. A
* position index of -1 would append the component to the end.
*
* It is overridden here to set the only
* component in this SplitterPanel. This is the component that is displayed
* in a panel. The SplitterPanel can only contain one component.
* Any previous component will be removed before the new one is added.
*
* @param comp the component to add
* @param pos the zero-relative index at which to add the component or -1
* for end (IGNORED)
* @return the added component
* @see #remove
*/
public synchronized Component add(Component comp, int pos)
{
if (comp == null)
return null;
if (isOuter)
return innerSP.add(comp, pos);
if (spComponent != null)
super.remove(spComponent);
spComponent = super.add(comp, -1);
layout();
return spComponent;
}
/**
* Adds a component to the end of this container.
* This is a standard Java AWT method which gets called to add a
* component to a container. Typically, the specified component is added to
* the end of this container.
*
* It is overridden here to set the only
* component in this SplitterPanel. This is the component that is displayed
* in a panel. The SplitterPanel can only contain one component.
* Any previous component will be removed before the new one is added.
*
* @param comp the component to add
* @return the added component
* @see #remove
*/
public Component add(Component comp) {
return this.add(comp, -1);
}
/**
* Removes the specified component from this container.
* This is a standard Java AWT method which gets called to remove a
* component from a container. When this happens the component's
* removeNotify() will also get called to indicate component removal.
*
* @param comp the component to remove
* @see #removeAll
* @see #add
*/
public synchronized void remove(Component comp)
{
if (isOuter)
{
innerSP.remove(comp);
}
else
{
if (comp == spComponent)
{
spComponent = null;
super.remove(comp);
}
else
{
if (sub1 != null)
sub1.remove(comp);
if (sub2 != null)
sub2.remove(comp);
}
}
}
/**
* Removes all the components from this container.
* This is a standard Java AWT method which gets called to remove all
* the components from a container. When this happens each component's
* removeNotify() will also get called to indicate component removal.
* <p>
* Split panels are preserved.
*
* @see #remove
* @see #add
*/
public synchronized void removeAll()
{
if (isOuter)
{
innerSP.removeAll();
}
else
{
super.removeAll();
if (sub1 != null)
{
super.add(sub1, -1);
sub1.removeAll();
}
if (sub2 != null)
{
super.add(sub2, -1);
sub1.removeAll();
}
}
spComponent = null;
}
/**
* Splits a SplitterPanel into two panels.
* This creates two new SplitterPanels which are nested within the original
* SplitterPanel. If the split type is SPLIT_VER the new panels are arranged
* side-by-side with a splitter bar vertically between them.
* If the split type is SPLIT_HOR the new panels are arranged one above the
* other with a splitter bar horizontally between them.
*
* @param splitType SPLIT_VER or SPLIT_HOR to request a
* vertical or horizontal split
* @return the first new SplitterPanel if successful (left or top)
* @see #SPLIT_VER
* @see #SPLIT_HOR
*/
public SplitterPanel split(int splitType)
{
return split(splitType, null, null);
}
/**
* Splits a SplitterPanel into two panels and adds two components.
* This creates two new SplitterPanels which are nested within the original
* SplitterPanel.
* <p>
* If the split type is SPLIT_VER the new panels are arranged
* side-by-side with a splitter bar vertically between them.
* Component theComp1 would be placed in the left panel, theComp2 in the right.
* <p>
* If the split type is SPLIT_HOR the new panels are arranged one above the
* other with a splitter bar horizontally between them.
* Component theComp1 would be placed in the top panel, theComp2 in the bottom.
*
* @param splitType SPLIT_VER or SPLIT_HOR to request
* vertical or horizontal splits
* @param theComp1 the first component (left or top)
* @param theComp2 the second component (right or bottom)
* @return the first new SplitterPanel if successful (left or top)
* @see #SPLIT_VER
* @see #SPLIT_HOR
*/
public synchronized SplitterPanel split(int splitType, Component theComp1, Component theComp2)
{
if (isOuter)
return(this.innerSP.split(splitType, theComp1, theComp2));
if (this.splitType != SPLIT_NONE)
return null;
if ((splitType != SPLIT_VER) && (splitType != SPLIT_HOR))
return null;
sub1 = new SplitterPanel(false);
if (sub1 == null)
return null;
sub2 = new SplitterPanel(false);
if (sub2 == null)
{
super.remove(sub1);
return null;
}
this.splitType = splitType;
sub1.outerSP = this.outerSP;
sub2.outerSP = this.outerSP;
super.add(sub1, -1);
super.add(sub2, -1);
if (theComp1 != null)
sub1.add(theComp1);
if (theComp2 != null)
sub2.add(theComp2);
propagateChanges();
return sub1;
}
/**
* Takes no action.
* This is a standard Java AWT method which gets called to specify
* which layout manager should be used to layout the components in
* standard containers.
*
* Since layout managers CANNOT BE USED with this container the standard
* setLayout has been OVERRIDDEN for this container and does nothing.
*
* @param l the layout manager to use to layout this container's components
* (IGNORED)
* @see java.awt.Container#getLayout
**/
public void setLayout(LayoutManager mgr)
{
}
/**
* Override this method if you want a behavior when events
* occur in the outer gap. Be sure to call super.oGapThis
* if you do override this method. Note that it is recommended
* that you place a SplitterPanel inside of a frame and set
* outer border and gap sizes to zero if you want the end user
* to be able to resize.
* @param x the x coordinate of where the event occured.
* @param y the y coordinate of where the event occured.
*/
public void oGapThis(int x, int y)
{
if (!doMoveSplit && !inGap(x, y))
resetCursor();
}
/**
* @deprecated
* @see #oGapThis(int, int)
*/
public boolean oGapThis(Event evt)
{
oGapThis(evt.x, evt.y);
return true;
}
/**
* Returns a string representation of this component.
* This is a standard Java AWT method which gets called to generate
* a string that represents this component.
*
* @return a meaningful string about this object
*/
public String toString()
{
String typeString = "Bad Panel ";
Container theParent;
String theParentString;
if (isOuter)
{
typeString = "Outer ";
}
else
{
if (splitType == SPLIT_NONE)
{
typeString = "Unsplit ";
}
if (splitType == SPLIT_VER)
{
typeString = "Vertically split ";
}
if (splitType == SPLIT_HOR)
{
typeString = "Horizontally split ";
}
}
theParent = getParent();
if (theParent == null)
theParentString = " with no parent (not added)";
else
theParentString = " with parent @" + theParent.hashCode();
return typeString + super.toString() + ": @" + hashCode() + theParentString;
}
/**
* Tells this component that it has been added to a container.
* This is a standard Java AWT method which gets called by the AWT when
* this component is added to a container. Typically, it is used to
* create this component's peer.
*
* It has been overridden here to hook-up event listeners.
*
* @see #removeNotify
*/
public synchronized void addNotify()
{
super.addNotify();
errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
//Hook up listeners
if (mouse == null)
{
mouse = new Mouse();
addMouseListener(mouse);
}
if (mouseMotion == null)
{
mouseMotion = new MouseMtn();
addMouseMotionListener(mouseMotion);
}
if (stVeto == null)
{
stVeto = new STVeto();
addSplitTypeListener(stVeto);
}
}
/**
* Tells this component that it is being removed from a container.
* This is a standard Java AWT method which gets called by the AWT when
* this component is removed from a container. Typically, it is used to
* destroy the peers of this component and all its subcomponents.
*
* It has been overridden here to unhook event listeners.
*
* @see #addNotify
*/
public synchronized void removeNotify()
{
//Unhook listeners
if (mouse != null)
{
removeMouseListener(mouse);
mouse = null;
}
if (mouseMotion != null)
{
removeMouseMotionListener(mouseMotion);
mouseMotion = null;
}
if (stVeto != null)
{
removeSplitTypeListener(stVeto);
stVeto = null;
}
super.removeNotify();
}
/**
* Checks to see if a given SplitType is a valid split type.
* @param testSplitType the split type to test
* @return true if testSplitType is acceptable, false if not.
*/
public boolean isValidSplitType(int testSplitType)
{
switch(testSplitType)
{
case SPLIT_NONE:
case SPLIT_VERTICAL:
case SPLIT_HORIZONTAL:
return true;
default:
return false;
}
}
/**
* Adds a listener for all event changes.
* @param listener the listener to add.
* @see #removePropertyChangeListener
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
{
changes.addPropertyChangeListener(listener);
}
/**
* Removes a listener for all event changes.
* @param listener the listener to remove.
* @see #addPropertyChangeListener
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
{
changes.removePropertyChangeListener(listener);
}
/**
* Adds a vetoable listener for all event changes.
* @param listener the listener to add.
* @see #removeVetoableChangeListener
*/
public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
{
vetos.addVetoableChangeListener(listener);
}
/**
* Removes a vetoable listener for all event changes.
* @param listener the listener to remove.
* @see #addVetoableChangeListener
*/
public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
{
vetos.removeVetoableChangeListener(listener);
}
/**
* Adds a property listener for SplitType changes.
* @param listener the listener to add.
* @see #removeSplitTypeListener
*/
public synchronized void addSplitTypeListener(PropertyChangeListener listener)
{
changes.addPropertyChangeListener("SplitType", listener);
}
/**
* Removes a property listener for SplitType changes.
* @param listener the listener to remove.
* @see #addSplitTypeListener
*/
public synchronized void removeSplitTypeListener(PropertyChangeListener listener)
{
changes.removePropertyChangeListener("SplitType", listener);
}
/**
* Adds a vetoable listener for SplitType changes.
* @param listener the listener to add.
* @see #removeSplitTypeListener
*/
public synchronized void addSplitTypeListener(VetoableChangeListener listener)
{
vetos.addVetoableChangeListener("SplitType", listener);
}
/**
* Removes a vetoable listener for SplitType changes.
* @param listener the listener to remove.
* @see #addSplitTypeListener
*/
public synchronized void removeSplitTypeListener(VetoableChangeListener listener)
{
vetos.removeVetoableChangeListener("SplitType", listener);
}
/**
* This is the Mouse Event handling innerclass.
*/
class Mouse extends java.awt.event.MouseAdapter implements java.io.Serializable
{
/**
* Handles Mouse Pressed events
* @param e the MouseEvent
*/
public void mousePressed(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
if (isOuter)
{
oGapThis(x, y);
}
else if ((splitType == SPLIT_HORIZONTAL) && outerSP.cursorChanged)
{
doMoveSplit = true;
moveFromX = x;
moveFromY = y;
}
else if ((splitType == SPLIT_VERTICAL) && outerSP.cursorChanged)
{
doMoveSplit = true;
moveFromX = x;
moveFromY = y;
}
else if (outerSP.cursorChanged)
{
Point p = location();
SplitterPanel parent = (SplitterPanel)getParent();
if(parent != null)
{
MouseListener parentListener = parent.mouse;//getMouseListener();
if(parentListener != null)
parentListener.mousePressed(new MouseEvent(cursorCh, e.getID(), e.getWhen(), e.getModifiers(), p.x + x, p.y + y, e.getClickCount(), e.isPopupTrigger()));
}
}
}
/**
* Handles Mouse Released events
* @param e the MouseEvent
*/
public void mouseReleased(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
if (isOuter)
{
oGapThis(x, y);
}
//Important -- Do NOT check that this is the target for mouse up
else if (doMoveSplit == true)
{
moveSplit(x - moveFromX, y - moveFromY);
doMoveSplit = false;
}
}
}
/**
* This is the MouseMotion Event handling innerclass.
*/
class MouseMtn implements java.awt.event.MouseMotionListener, java.io.Serializable
{
/**
* Handles Mouse Moved events
* @param e the MouseEvent
*/
public void mouseMoved(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
if (isOuter)
{
oGapThis(x, y);
}
else if ((splitType == SPLIT_HORIZONTAL) && inGap1(x, y))
{
setCursor(moveSplitCursor);
}
else if ((splitType == SPLIT_VERTICAL) && inGap1(x, y))
{
setCursor(moveSplitCursor);
}
else
{
if (!doMoveSplit && !inGap(x, y)) resetCursor();
}
}
/**
* Handles Mouse Dragged events
* @param e the MouseEvent
*/
public void mouseDragged(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
if (isOuter)
{
oGapThis(x, y);
}
else if (doMoveSplit == true)
{
setCursor(moveSplitCursor);
if(allowDynamicMoving)
{
moveSplit(x - moveFromX, y - moveFromY);
moveFromX = x;
moveFromY = y;
}
}
}
}
/**
* This is the PropertyChangeEvent handling inner class for the constrained SplitType property.
* Handles vetoing SplitTypes that are not valid.
*/
class STVeto implements java.beans.VetoableChangeListener, java.io.Serializable
{
/**
* This method gets called when an attempt to change the constrained SplitType property is made.
* Ensures the given SplitType is valid for this component.
*
* @param e a <code>PropertyChangeEvent</code> object describing the
* event source and the property that has changed.
* @exception PropertyVetoException if the recipient wishes the property
* change to be rolled back.
*/
public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException
{
int i = ((Integer)e.getNewValue()).intValue();
if (!isValidSplitType(i))
{
throw new PropertyVetoException(errors.getString("InvalidSplitType") + i, e);
}
}
}
/**
* Returns the mouse event handling object.
*/
protected MouseListener getMouseListener()
{
return mouse;
}
/**
* Utility method to calculate the optimum width or height of a sub-panel.
*/
protected int optSize(int total, int _gap, int _min1, int _min2, int _pref1, int _pref2)
{
int diff;
float scale;
int gap;
int min1;
int min2;
int pref1;
int pref2;
gap = Math.max(_gap, 0);
min1 = Math.max(_min1, 1);
min2 = Math.max(_min2, 1);
pref1 = Math.max(_pref1, min1);
pref2 = Math.max(_pref2, min2);
diff = total - (pref1 + pref2 + gap);
if (diff >= 0)
{
scale = (float)pref1/(float)(pref1 + pref2);
return pref1 + (int)(diff*scale);
}
diff = total - (min1 + min2 + gap);
if (diff >= 0)
{
scale = (float)pref1/(float)(pref1 + pref2);
return min1 + (int)(diff*scale);
}
if (total > gap)
{
scale = (float)min1/(float)(min1 + min2);
return min1 + (int)(diff*scale);
}
return total;
}
/**
* Utility method to lay out this component.
*/
protected void placeComponents()
{
Dimension prefDim1;
Dimension prefDim2;
Dimension minDim1;
Dimension minDim2;
int sub1X;
int sub1Y;
int sub2X;
int sub2Y;
int sub1Width;
int sub1Height;
int sub2Width;
int sub2Height;
Rectangle rect = new Rectangle(curLoc.x, curLoc.y, curDim.width, curDim.height);
if (isOuter)
{
sub1X = oGapWidth + oBdrSize;
sub1Y = oGapHeight + oBdrSize;
sub1Width = rect.width - ((2 * oGapWidth + oBdrSize +1 ) );
sub1Height = rect.height - ((2 * oGapHeight + oBdrSize + 1) );
innerSP.reshape(sub1X, sub1Y, sub1Width, sub1Height);
}
else
{
if (sub1 == null)
{
minDim1 = new Dimension(1, 1);
prefDim1 = new Dimension(1, 1);
}
else
{
minDim1 = sub1.minimumSize();
prefDim1 = sub1.preferredSize();
}
if (sub2 == null)
{
minDim2 = new Dimension(1, 1);
prefDim2 = new Dimension(1, 1);
}
else
{
minDim2 = sub2.minimumSize();
prefDim2 = sub2.preferredSize();
}
switch (splitType)
{
case SPLIT_NONE:
{
if (spComponent != null)
{
sub1X = iBdrSize;
sub1Y = iBdrSize;
sub1Width = rect.width - (2 * iBdrSize);
sub1Height = rect.height - (2 * iBdrSize);
if (sub1Width < 0)
sub1Width = 0;
if (sub1Height < 0)
sub1Height = 0;
if (propResize) spComponent.reshape(sub1X, sub1Y, sub1Width, sub1Height);
else spComponent.move(sub1X, sub1Y);
spComponent.layout();
spComponent.invalidate();
spComponent.validate();
try {
spComponent.repaint();
}
catch(Exception e) {
repaint();
}
}
return;
}
case SPLIT_VER:
{
sub1X = 0;
sub1Y = 0;
sub1Width = optSize(rect.width, iGapWidth, minDim1.width, minDim2.width, prefDim1.width, prefDim2.width);
sub1Height = rect.height;
sub2X = sub1Width + iGapWidth;
sub2Y = 0;
sub2Width = rect.width - sub2X;
sub2Height = rect.height;
break;
}
case SPLIT_HOR:
{
sub1X = 0;
sub1Y = 0;
sub1Width = rect.width;
sub1Height = optSize(rect.height, iGapHeight, minDim1.height, minDim2.height, prefDim1.height, prefDim2.height);
sub2X = 0;
sub2Y = sub1Height + iGapHeight;
sub2Width = rect.width;
sub2Height = rect.height - sub2Y;
break;
}
default:
return;
}
if (heightOnly)
{
heightOnly = false;
sub1.reshapeHeight(sub1Y, sub1Height);
sub2.reshapeHeight(sub2Y, sub2Height);
}
else if (widthOnly)
{
widthOnly = false;
sub1.reshapeWidth(sub1X, sub1Width);
sub2.reshapeWidth(sub2X, sub2Width);
}
else
{
sub1.reshape(sub1X, sub1Y, sub1Width, sub1Height);
sub2.reshape(sub2X, sub2Y, sub2Width, sub2Height);
}
sub1.invalidate();
sub1.validate();
sub1.repaint();
sub2.invalidate();
sub2.validate();
sub2.repaint();
}
}
/**
* Utility method to draw the border.
*/
protected void draw3DBdr(Graphics g, int x, int y, int width, int height, int bdrSize, Color c, boolean raised)
{
int i;
if (bdrSize < 0)
return;
g.setColor(use3dBdr ? c : c.darker());
for (i = 0; i < bdrSize; i++)
{
if (use3dBdr) g.draw3DRect(x + i, y + i, width - 2*i, height - 2*i, raised);
else g.drawRect(x + i, y + i, width - 2*i, height - 2*i);
}
}
/**
* Utility method to update the gap/border/etc properties in sub-panels.
*/
protected void propagateChanges()
{
if (innerSP != null)
{
propagateChangesSP(innerSP);
innerSP.propagateChanges();
}
if (sub1 != null)
{
propagateChangesSP(sub1);
sub1.propagateChanges();
}
if (sub2 != null)
{
propagateChangesSP(sub2);
sub2.propagateChanges();
}
}
/**
* Utility method to update the gap/border/etc properties in another panel.
* The current property values of this object are copied into the
* target object's properties.
* @param spTarget the panel to update the properties of
*/
protected void propagateChangesSP(SplitterPanel spTarget)
{
spTarget.gapColor = this.gapColor;
spTarget.enforceMinDim = this.enforceMinDim;
spTarget.moveSplitCursor = this.moveSplitCursor;
spTarget.propResize = this.propResize;
if ((spTarget.oGapWidth != this.oGapWidth) ||
(spTarget.iGapWidth != this.iGapWidth) ||
(spTarget.oGapHeight != this.oGapHeight) ||
(spTarget.iGapHeight != this.iGapHeight) ||
(spTarget.iBdrSize != this.iBdrSize) ||
(spTarget.oBdrSize != this.oBdrSize) ||
(spTarget.use3dBdr != this.use3dBdr) ||
(spTarget.allowDynamicMoving != this.allowDynamicMoving))
{
spTarget.oGapWidth = this.oGapWidth;
spTarget.iGapWidth = this.iGapWidth;
spTarget.oGapHeight = this.oGapHeight;
spTarget.iGapHeight = this.iGapHeight;
spTarget.iBdrSize = this.iBdrSize;
spTarget.oBdrSize = this.oBdrSize;
spTarget.use3dBdr = this.use3dBdr;
spTarget.allowDynamicMoving = this.allowDynamicMoving;
layout();
spTarget.layout();
}
}
/**
* Utility method to create a new Dimension that encompasses the
* two input Dimensions.
* @param dim1 a dimension
* @param dim2 another dimension
* @return a Dimension that has the larger of each input dimensions axis
*/
protected Dimension maxDimOf(Dimension dim1, Dimension dim2)
{
return new Dimension(Math.max(dim1.width, dim2.width), Math.max(dim1.height, dim2.height));
}
/**
* Utility method to set the cursor to the default one.
*/
protected void resetCursor()
{
if (outerSP.cursorChanged == false)
return;
try
{
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
outerSP.cursorChanged = false;
cursorCh = null;
}
catch(IllegalArgumentException e)
{
return;
}
}
/**
* Sets the currently used cursor.
* @param cursor the new cursor to use
*/
public synchronized void setCursor(Cursor cursor)
{
if (outerSP.cursorChanged)
return;
if (isOuter)
{
try
{
super.setCursor(cursor);
cursorChanged = true;
}
catch(IllegalArgumentException e)
{
}
}
else
{
outerSP.setCursor(cursor);
}
}
/**
* Utility method to determine if the given location is within
* the given component.
* @param comp the component
* @param x the horizontal location
* @param y the vertical location
* @return true if the location is within the component
*/
protected boolean inComponent(Component comp, int x, int y)
{
//Point compLoc = comp.location();
Rectangle r = comp.bounds();
r.grow(-iBdrSize-1, -iBdrSize-1);
//return comp.inside(x - compLoc.x, y - compLoc.y);
return ((x-r.x >= 0) && (x-r.x <= r.width) && (y-r.y >= 0) && (y-r.y <= r.height));
}
/**
* Utility method to determine if the given location is in the gap
* between any nested sub-panels.
* @param evtX the horizontal location
* @param evtY the vertical location
* @return true if the location is within the gap
*/
protected boolean inGap(int evtX, int evtY)
{
if (inGap1(evtX, evtY))
return true;
if (isOuter)
{
return innerSP.inGap(evtX - innerSP.curLoc.x, evtY - innerSP.curLoc.y);
}
else if ((splitType == SPLIT_HOR) || (splitType == SPLIT_VER))
{
return (sub1.inGap(evtX - sub1.curLoc.x, evtY - sub1.curLoc.y) ||
sub2.inGap(evtX - sub2.curLoc.x, evtY - sub2.curLoc.y));
}
return false;
}
/**
* Utility method to determine if the given location is in the gap
* between this panel's sub-panels.
* @param evtX the horizontal location
* @param evtY the vertical location
* @return true if the location is within the gap
*/
protected boolean inGap1(int evtX, int evtY)
{
if (!inside(evtX, evtY))
return false;
if (isOuter)
{
return false;
}
if (inOutBoarder(evtX, evtY)) {
return false;
}
else if ((splitType == SPLIT_HOR) || (splitType == SPLIT_VER))
{
if (inComponent(sub1, evtX, evtY) || (inComponent(sub2, evtX, evtY)))
{
return false;
}
else
{
if (inComponent(this, evtX, evtY)) cursorCh = this;
return true;
}
}
else
{
return false;
}
}
/**
* Utility method to determine if the given location is in the
* border around this component.
* @param evtX the horizontal location
* @param evtY the vertical location
* @return true if the location is within the border
*/
protected boolean inOutBoarder(int evtX, int evtY) {
Component sp = this;
Rectangle r = sp.bounds();
int x = r.x;
int y = r.y;
while(sp != outerSP) {
sp = sp.getParent();
r = sp.bounds();
x += r.x;
y += r.y;
}
r.grow(-(outerSP.iBdrSize + outerSP.oBdrSize + outerSP.oGapWidth/2+4), -(outerSP.iBdrSize + outerSP.oBdrSize + outerSP.oGapHeight/2+4));
r.move(r.x + outerSP.oGapWidth/2, r.y + outerSP.oGapHeight/2);
return !r.inside(x + evtX, y + evtY);
}
/**
* Utility method to constrain the gap width/height inside the border to
* good values.
*/
protected void adjustOGapBdr()
{
if (this.oGapWidth < this.oBdrSize)
this.oGapWidth = Math.max(0, this.oBdrSize);
if (this.oGapHeight < this.oBdrSize)
this.oGapHeight = Math.max(0, this.oBdrSize);
}
/**
* This flag specifies how to draw the border around panels.
* If true, the border around the panel is drawn with a three-dimensional effect.
*/
protected boolean use3dBdr = true;
/**
* Determines the cursor to use when dragging the split between panels.
* Use Cursor.HAND_CURSOR, etc to choose a cursor.
* @see java.awt.Cursor
*/
protected Cursor moveSplitCursor = new Cursor(Cursor.MOVE_CURSOR);
/**
* Dimension variables.
* Note that these should be set explicitly if you wish to override
* the built in calculation of these dimensions. For instance, do
* not set (or set to zeros), the value for prefDim if you want the
* SplitterPanel to calculate this as the sum of the subcomponents
* prefDims. minDim is used when the user explicitly sets the
* minimum size; prefDim is used when the user explicitly sets the
* preferred size.
* @see #prefDim
*/
protected Dimension minDim;
/**
* Dimension variables.
* Note that these should be set explicitly if you wish to override
* the built in calculation of these dimensions. For instance, do
* not set (or set to zeros), the value for prefDim if you want the
* SplitterPanel to calculate this as the sum of the subcomponents
* prefDims. minDim is used when the user explicitly sets the
* minimum size; prefDim is used when the user explicitly sets the
* preferred size.
* @see #minDim
*/
protected Dimension prefDim;
/**
* Current component dimensions.
*/
protected Dimension curDim;
/**
* The split width between panels, in pixels.
*/
protected int iGapWidth;
/**
* The split height between panels, in pixels.
*/
protected int iGapHeight;
/**
* The gap width inside the border, in pixels.
*/
protected int oGapWidth;
/**
* The gap height inside the border, in pixels.
*/
protected int oGapHeight;
/**
* The size of the border drawn between panels, in pixels.
*/
protected int iBdrSize;
/**
* The size of the border drawn around the outside of the SplitterPanel,
* in pixels.
*/
protected int oBdrSize;
/**
* The type of split for this SplitterPanel.
* Value is one of: SPLIT_VERTICAL or SPLIT_HORIZONTAL.
* @see #SPLIT_VERTICAL
* @see #SPLIT_HORIZONTAL
* @see #SPLIT_NONE
*/
protected int splitType;
/**
* Internal use only.
*/
protected int moveFromX;
/**
* Internal use only.
*/
protected int moveFromY;
/**
* Panel contains no subpanels, initial state.
*/
protected static final int SPLIT_NONE = 0;
/**
* Internal use only.
*/
transient protected boolean bOsFlag;
/**
* Enforce minimum dimension mode flag.
* This mode prevents the dragging of a split between panels
* from making a panel smaller than the minimum size of its component.
* If true, enforces minimum panel dimensions.
* @see #isEnforceMinDim
* @see #setEnforceMinDim
*/
protected boolean enforceMinDim = false;
/**
* The "resize propagation" mode flag.
* If this is false, resizing of SplitterPanel
* panels does not automatically call
* the contained component's resize() method.
* The default behavior propagates the resizing effect of dragging the
* split between panes by calling move and resize for the panels that have
* been added to those panes. Clearing this introduces a clipping effect,
* rather than a scaling effect.
* @see #isPropResize
* @see #setPropResize
*/
protected boolean propResize = true;
/**
* Internal use only.
*/
protected boolean isOuter;
/**
* Internal flag indicating a reshape in height only.
*/
protected boolean heightOnly;
/**
* Internal flag indicating a reshape in width only.
*/
protected boolean widthOnly;
/**
* Internal flag indicating split being moved.
*/
transient protected boolean doMoveSplit = false;
/**
* Internal use only.
*/
transient protected boolean cursorChanged = false;
/**
* This flag specifies if moving of a split in a splitter panel will be real time,
* or delayed until mouse released.
* @see #isAllowDynamicMoving
* @see #setAllowDynamicMoving
*/
protected boolean allowDynamicMoving;
/**
* Sub-panel 1 of 2.
*/
protected SplitterPanel sub1;
/**
* Sub-panel 2 of 2.
*/
protected SplitterPanel sub2;
/**
* Internal use only.
*/
protected SplitterPanel innerSP;
/**
* Internal use only.
*/
protected SplitterPanel outerSP;
/**
* Internal use only.
*/
protected static SplitterPanel cursorCh = null;
/**
* The current location of this component.
*/
protected Point curLoc;
/**
* The component contained within this panel.
*/
protected Component spComponent;
/**
* The color of the gap between panels and around the outside
* border.
* @see #getGapColor
* @see #setGapColor
*/
protected Color gapColor;
/**
* Error strings.
*/
transient protected ResourceBundle errors;
private Mouse mouse = null;
private MouseMtn mouseMotion = null;
private STVeto stVeto = null;
private symantec.itools.beans.VetoableChangeSupport vetos = new symantec.itools.beans.VetoableChangeSupport(this);
private symantec.itools.beans.PropertyChangeSupport changes = new symantec.itools.beans.PropertyChangeSupport(this);
}